using Bowling.Constants;
using Bowling.Enums;

namespace Bowling;

public class BowlingGame : IVisualizable
{
    public BowlingGame()
    {
        Frames = new List<Frame>();
        Frames.AddRange(Enumerable.Range(0, BowlingConstant.FrameNumber - 1).Select(i => new RegularFrame()));
        Frames.Add(new LastFrame());
    }

    public void Record(int pins)
    {
        if (pins < 0 || pins > 10)
        {
            DeliveryStatus = DeliveryStatus.Failure;
            ErrorRecord = new ErrorRecord
            {
                Pins = pins,
                ErrorType = ErrorType.OutOfRange
            };

            return;
        }

        var currentFrame = Frames[FrameOrder];

        if (currentFrame.Progress == FrameProgress.NotStarted || currentFrame.Progress == FrameProgress.InProgress)
        {
            currentFrame.PinsRecord.Add(pins);
            return;
        }

        if (currentFrame is LastFrame)
        {
            DeliveryStatus = DeliveryStatus.Failure;
            ErrorRecord = new ErrorRecord
            {
                Pins = pins,
                ErrorType = ErrorType.HasEnded
            };

            return;
        }
        else
        {
            FrameOrder += 1;

            currentFrame = Frames[FrameOrder];
            currentFrame.PinsRecord.Add(pins);
        }

        if (DeliveryStatus != DeliveryStatus.Succeed)
        {
            DeliveryStatus = DeliveryStatus.Succeed;
            ErrorRecord = null;
        }
    }

    private int FrameOrder { get; set; } = 0;

    internal List<Frame> Frames { get; set; } 

    private DeliveryStatus DeliveryStatus { get; set; } = DeliveryStatus.Succeed;

    private ErrorRecord? ErrorRecord { get; set; }

    public string Visualize()
    {
        string content = string.Concat(Frames.Select(f => f as IVisualizable).Select(f => f.Visualize()));

        if (DeliveryStatus == DeliveryStatus.Failure && ErrorRecord is not null)
        {
            var errorMessage = ErrorRecord.ErrorType switch 
            {
                ErrorType.OutOfRange => $"Error: incorrect knocked down number - {ErrorRecord.Pins}",
                ErrorType.HasEnded => "Error: the game has ended",
                _ => string.Empty
            };
            
            content += Environment.NewLine + errorMessage;
        }

        return content;
    }
}
